/*
 * Copyright 2014 OGIS-RI All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jp.co.ogis_ri.optaplanner.core.api.solver;

import java.util.Date;
import java.util.concurrent.Callable;

import org.optaplanner.core.api.domain.solution.Solution;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.api.solver.event.BestSolutionChangedEvent;
import org.optaplanner.core.api.solver.event.SolverEventListener;
import org.optaplanner.core.config.solver.random.RandomType;
import org.optaplanner.core.impl.solver.DefaultSolver;
import org.optaplanner.core.impl.solver.random.DefaultRandomFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SolutionTask implements Callable<Solution>, SolverEventListener {

	protected final transient Logger logger = LoggerFactory.getLogger(this
			.getClass());

	SolverFactory solverFactory;

	Solution unsolvedSolution;

	private long randomSeed;

	private Solver solver;

	Object lockObject = new Object();

	private boolean isTerminateEarly;

	long startDateTime;

	String taskName;

	public SolutionTask(final SolverFactory solverFactory,
			final Solution unsolvedSolution, final long randomSeed,
			final String taskName) {
		super();
		this.solverFactory = solverFactory;
		this.unsolvedSolution = unsolvedSolution;
		this.randomSeed = randomSeed;
		this.taskName = taskName;

		this.isTerminateEarly = false;
	}

	@Override
	public Solution call() throws Exception {

		synchronized (this.lockObject) {
			if (this.isTerminateEarly) {
				return null;
			}

			this.solver = this.solverFactory.buildSolver();

			// リスナー登録
			this.solver.addEventListener(this);

			// 乱数のシード設定
			((DefaultSolver) this.solver)
					.setRandomFactory(new DefaultRandomFactory(
							RandomType.MERSENNE_TWISTER, this.randomSeed));

			this.startDateTime = new Date().getTime();
		}

		this.solver.solve(this.unsolvedSolution);

		return this.solver.getBestSolution();
	}

	@Override
	public void bestSolutionChanged(final BestSolutionChangedEvent event) {
		this.logger.debug("TaskName:" + this.taskName + " " + "ElapsedTime:"
				+ (new Date().getTime() - this.startDateTime) + "ms BestScore:"
				+ event.getNewBestSolution().getScore().toString());
	}

	public void terminateEarly() {

		synchronized (this.lockObject) {
			this.isTerminateEarly = true;

			if (this.solver != null) {
				this.solver.terminateEarly();
			}
		}
	}

}
